Repository navigation
Conversation
With a projection, almost all the time of a contour plot went into GeoViews projecting the lines one at a time through Cartopy while rendering: ~13 of the ~17 s for a noisy field on a 786k-face mesh. GeoViews does this for any element that has a crs, even one already in the plot's projection, and a plain HoloViews element would lose GeoViews' map extents and options such as global_extent. _project_lines replaces _drop_lines_outside_projection. It projects all points and segment midpoints at once and applies Cartopy's own test, from cartopy/trace.pyx, to every segment: the projected midpoint within the projection's threshold of the segment, every point on the map, and the segment covered by the map when the map is not convex. Lines that pass are exactly what Cartopy gives; only the others, such as those that cross the edge of the map or a seam of the projection, are projected by Cartopy. The result matched Cartopy line by line, with no difference in any coordinate, for 10 projections on four datasets and both methods. The lines are a subclass of gv.Contours with crs=projection, plotted by GeoViews' own GeoContourPlot with its projection step turned off. It is named Contours to share the options of contour lines, created on first use since GeoViews is optional, and found by pickle through a module __getattr__. Noisy field, 786k faces, contour() + render: Robinson, Bokeh 17.1 s -> 2.3 s Robinson, Matplotlib 17.1 s -> 2.3 s Orthographic, Bokeh 16.1 s -> 5.7 s GeoViews now frames a plot of the lines alone on the projected lines, rather than on the projection of their longitude-latitude bounding box, so the extent of some maps (Lambert conformal, orthographic) changes slightly; the lines themselves are the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test that decides whether a projected segment can be kept as it is was adapted from straightAndDomain() in lib/cartopy/trace.pyx of Cartopy 0.25.0: the position along the segment and the separation of the projected midpoint from it, the tolerance that narrows towards the ends of the segment, and the requirement that the map cover the segment. Cartopy is released under the BSD 3-Clause License, which requires that redistributed source keep its copyright notice, conditions and disclaimer. The notice is reproduced verbatim above _project_lines, where it travels with the source into every distribution, and the docstring and comments name where the test comes from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an orthographic map about half the lines of a field are on the far side of the globe, and none of their points can be projected. They were still given to Cartopy one at a time, only to come back empty. For a segment whose ends both cannot be projected, Cartopy's bisection in lib/cartopy/trace.pyx accepts the end at once and _project_segment() adds nothing, without looking at the points in between. A line with no point that can be projected is therefore never on the map, and is now dropped from the points already projected. The result still matches Cartopy line by line for 10 projections on four datasets and both methods, and projecting the lines of a 197k-face noisy field on an orthographic map takes 0.85 s instead of 1.9 s. The rule is credited to Cartopy next to the segment test, under the same BSD 3-Clause notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tour The "interpolated" method handed its triangulation to Matplotlib's tricontour, which spent ~2.2 s of its ~2.6 s on a 786k-face mesh setting up the neighbours of every triangle before contouring. _crossed_edges, compiled with numba (cache=True, nogil=True), finds the two edges where a level crosses each triangle that it crosses. Each crossing point is known by its edge, which the triangles on either side share, so the pieces are joined into lines by the existing _join_segments kernel, and the points are interpolated along their edges all at once. The triangulation is unchanged. The lines are the same segments as tricontour's, to within 1e-9, on eight datasets including node-centered data and faces with repeated nodes; tricontour also left a few slivers ~1e-14 long on the antimeridian, which are gone. A closed loop may start at a different point, and a loop that crosses the antimeridian is then cut there instead, which draws the same. Contouring a noisy field on the 786k-face mesh takes 0.68 s instead of 3.4 s, of which 0.3 s is the triangulation. The splitting of lines at the antimeridian, shared with the edges method, moves to _split_lines_at_antimeridian. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
contour-maps frames the lines by the area of the grid, set as their extents, so that they never widen a shaded plot they are overlaid on. Since the lines are projected before GeoViews sees them, HoloViews framed the lines and their labels by the projected line data instead, which reach closer to the antimeridian than the shaded faces: a Robinson map of a field whose lines reach the edge of the map was 34 km wider on each side than the shaded plot. The projected area of the grid is now also the default xlim and ylim of the lines, which HoloViews keeps to; options passed to .plot.contour() still win. The frame then equals the shaded plot's own on Robinson, Robinson centred on 180, orthographic, Lambert conformal and PlateCarree maps, alone and overlaid, with the frame sizes and label positions of contour-maps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #XXX
Overview
Second round of contour map optimization. This PR uses algorithms from cartopy, so we should be sure about the licensing situation before merging.
PR Checklist
General
Testing & Benchmarking
Documentation and Examples
docs/api.rst; internal (private) function names start with an underscore (_)AI Disclosure
AI Usage: Claude Opus 5.5